﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.DataMining
{
    /// <summary>
    /// This class provides the creation of a covariance matrix out of a data matrix.
    /// </summary>
    [Serializable]
    public class CovarianceMatrixCreator
    {
        /// <summary>
        /// The sourcematrix, which should be transformed to a covariance matrix.
        /// </summary>
        private Matrix sourceMatrix;

        /// <summary>
        /// Initializes a new instance of the <see cref="CovarianceMatrixCreator"/> class.
        /// </summary>
        /// <param name="sourceMatrix">The sourcematrix, which should be transformed to a covariance matrix.</param>
        public CovarianceMatrixCreator(Matrix sourceMatrix)
        {
            if (sourceMatrix == (Matrix) null)
            {
                throw new ArgumentNullException("sourceMatrix");
            }

            this.sourceMatrix = sourceMatrix;
        }

        /// <summary>
        /// Gets or sets zhe sourcematrix, which should be transformed to a covariance matrix.
        /// </summary>
        /// <value>The sourcematrix, which should be transformed to a covariance matrix.</value>
        public Matrix SourceMatrix
        {
            get { return sourceMatrix; }
            set { sourceMatrix = value; }
        }

        /// <summary>
        /// Creates the covariance matrix out of the specified matrix.
        /// </summary>
        /// <returns>The created covariance matrix.</returns>
        public Matrix CreateCovarianceMatrix()
        {
            double[] averageValues = new double[this.sourceMatrix.Columns];
            Matrix result = new Matrix(this.sourceMatrix.Columns, this.sourceMatrix.Columns);

            for (int i = 0; i < this.sourceMatrix.Columns; i++)
            {
                double tempuri = 0;

                for (int j = 0; j < this.sourceMatrix.Rows; j++)
                {
                    tempuri += this.sourceMatrix.GetValueAtPosition(j, i);
                }

                averageValues[i] = tempuri / this.sourceMatrix.Rows;
            }

            for (int i = 0; i < this.sourceMatrix.Columns; i++)
            {
                for (int j = 0; j < this.sourceMatrix.Columns; j++)
                {
                    if (j <= i)
                    {
                        double cov = this.ComputeCovariance(i, averageValues[i], j, averageValues[j]);

                        result.SetValueAtPosition(i, j, cov);
                        result.SetValueAtPosition(j, i, cov);
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Computes the covariance of the specified vectors.
        /// </summary>
        /// <param name="columnX">The vector x.</param>
        /// <param name="averageX">The average value of the vector x.</param>
        /// <param name="columnY">The vector y.</param>
        /// <param name="averageY">The average value of the vector y.</param>
        /// <returns>The covariance value of the two vectors.</returns>
        private double ComputeCovariance(int columnX, double averageX, int columnY, double averageY)
        {
            double result = 0;

            for (int i = 0; i < this.sourceMatrix.Rows; i++)
            {
                result += (this.sourceMatrix.GetValueAtPosition(i, columnX) - averageX)
                          * (this.sourceMatrix.GetValueAtPosition(i, columnY) - averageY);
            }

            return (1.0 / (this.sourceMatrix.Rows - 1)) * result;
        }
    }
}